﻿using System;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using System.Collections;
using System.Drawing.Design;

namespace DatabaseGroup.WinForms.UserControls
{
    /// <summary>
    /// Provides simple data binding for the CheckedListBox control
    /// </summary>
    [ToolboxBitmap(typeof(CheckedListBox))]
    public class BindableCheckedListBox : CheckedListBox
    {
        private object _dataSource;
        private string _displayMember;
        private string _checkedMember;
        private PropertyDescriptor _displayProperty;
        private PropertyDescriptor _checkedProperty;

        /// <summary>
        /// Gets or sets the name of the property that contains the data
        /// to display in the CheckedListBox
        /// </summary>
        [DefaultValue("")]
        [TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
        [Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        [Browsable(true)]
        public new string DisplayMember
        {
            get
            {
                return _displayMember;
            }
            set
            {
                _displayMember = value;
                TryDataBinding();
            }
        }

        /// <summary>
        /// Gets or sets the name of the boolean property that defines if the
        /// datasource item should be checked or not
        /// </summary>
        [DefaultValue("")]
        [TypeConverter("System.Windows.Forms.Design.DataMemberFieldConverter, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
        [Editor("System.Windows.Forms.Design.DataMemberFieldEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        [Browsable(true)]
        public string CheckedMember
        {
            get
            {
                return _checkedMember;
            }
            set
            {
                _checkedMember = value;
                TryDataBinding();
            }
        }

        /// <summary>
        /// Gets or sets the IList data source and binds the component items to the list
        /// </summary>
        [DefaultValue("")]
        [AttributeProvider(typeof(IListSource))]
        [RefreshProperties(RefreshProperties.All)]
        [Browsable(true)]
        public new object DataSource
        {
            get
            {
                return _dataSource;
            }
            set
            {
                if (value is IList)
                {
                    _dataSource = value;
                    TryDataBinding();
                }
            }
        }

        /// <summary>
        /// Attempts to bind the list of items within the control
        /// to the IList datasource
        /// </summary>
        private void TryDataBinding()
        {
            if (_dataSource == null || _displayMember == null || _checkedMember == null)
                return;

            this.Items.Clear();

            if (_displayProperty == null || _checkedProperty == null)
            {
                if (((IList)_dataSource).Count < 1)
                    return;

                object item = ((IList) _dataSource)[0];

                PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(item);
                _displayProperty = properties.Find(_displayMember, false);

                if (_displayProperty == null)
                    throw new MissingMemberException("Failed to find DisplayMember on DataSource item");

                _checkedProperty = properties.Find(_checkedMember, false);

                if (_checkedProperty == null)
                    throw new MissingMemberException("Failed to find CheckedMember on DataSource item");

                if (_checkedProperty.PropertyType != typeof(bool) ||
                    _checkedProperty.PropertyType != typeof(Boolean))
                    throw new InvalidCastException("CheckedMember property must be a boolean type");
            }

            foreach (object item in (IList)_dataSource)
            {
                this.Items.Add(_displayProperty.GetValue(item), (bool)_checkedProperty.GetValue(item));
            }
        }

        /// <summary>
        /// Raises the ItemCheck event, whilst also updating the underlying
        /// datasource (if available) with the updated checked status
        /// </summary>
        /// <param name="ice"></param>
        protected override void OnItemCheck(ItemCheckEventArgs ice)
        {
            if (_dataSource != null)
            {
                object item = ((IList) _dataSource)[ice.Index];
                _checkedProperty.SetValue(item, ice.NewValue == CheckState.Checked);
            }
                
            base.OnItemCheck(ice);
        }
    }
}
